package com.yawn.zk.controller;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ZkLockController {

    @Autowired
    private CuratorFramework curator;


    @GetMapping("t")
    public String lock() {
        SaleGoods saleGoods = new SaleGoods();
        for (int i = 0; i < 10; i++) {
            new Thread(saleGoods).start();
        }
        return "okay!";
    }



    class SaleGoods implements Runnable {
        private int restNum = 100;
        @Override
        public void run() {
            // 若还有余量，就一直购买
            while (restNum > 0) {
                try {
                    buyWithLock();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        /**
         *  1.客户端连接zookeeper，并在/lock下创建临时的且有序的子节点，
         * 第一个客户端对应的子节点为/lock/***-lock-0000000000，第二个
         * 为/lock/***-lock-0000000001，以此类推。
         *  2.客户端获取/lock下的子节点列表，判断自己创建的子节点是否为
         * 当前子节点列表中序号最小的子节点，如果是则认为获得锁，否则
         * 监听刚好在自己之前一位的子节点删除消息，获得子节点变更通知
         * 后重复此步骤直至获得锁；
         *  3.执行业务代码；
         *  4.完成业务流程后，删除对应的子节点释放锁。
         *
         *  此锁能保证公平性，非公平锁的实现可参考：
         * @see ZkLock2Controller
         */
        private void buyWithLock() throws Exception {
            //创建分布式锁, 锁空间的根节点路径为/lock
            InterProcessMutex lock = new InterProcessMutex(curator, "/lock");
            lock.acquire();
            //获得了锁, 进行业务流程
            buy();
            //完成业务流程, 释放锁
            lock.release();
        }

        void buy() {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (restNum > 0) {
                System.out.println(Thread.currentThread().getName()
                        + "买到一件，还剩：" + --restNum);
            } else {
                System.out.println(Thread.currentThread().getName()
                        + "没有买到，还剩：" + restNum + "，我们深感抱歉！");
            }

        }
    }


}
